#pragma once
#include <iostream>
#include <sstream>
#include <fstream>
#include <memory>
#include <cassert>
#include <cstdlib>
#include "util.hpp"

namespace ckflogs
{
    // 日志落地方向
    // 1.stdout
    // 2.文件
    // 3.滚动文件

    // 抽象一个日志落地父类，然后不同的落地方向派生不同的子类

    class LogSink
    {
    public:
        using Ptr = std::shared_ptr<LogSink>;

    public:
        LogSink() {}
        virtual ~LogSink() {}
        virtual void sink(const char *log, size_t len) = 0;
    };

    class StdOutLogSink : public LogSink
    {
    public:
        void sink(const char *log, size_t len)
        {
            std::cout.write(log, len);
        }
    };

    class FileLogSink : public LogSink
    {
    private:
        std::string pathname_;
        std::ofstream ofs_;

    public:
        FileLogSink(const std::string &pathname) : pathname_(pathname)
        {
            // 1.查看pathname的目录是否存在，不存在要创建
            std::string dir = util::File::getDirctory(pathname_);
            if (!util::File::exist(dir))
            {
                // 2.创建目录
                if (!util::File::createDirctory(dir))
                {
                    std::cout << "create dirctory fail..." << std::endl;
                    exit(1);
                }
            }
            // 3.打开文件
            ofs_.open(pathname_, std::ofstream::binary | std::ofstream::app);
        }
        ~FileLogSink()
        {
            if (ofs_.is_open())
                ofs_.close();
        }
        void sink(const char *log, size_t len)
        {
            ofs_.write(log, len);
        }
    };

    // 滚动文件
    // 按文件大小滚动，每个文件最多存储1MB，超过1MB则关闭本文件并打开一个新文件
    static const size_t default_max_size = 1024 * 1024;
    class RollBySizeLogSink : public LogSink
    {
    private:
        std::string prefix_;    // 滚动文件名的前缀 （固定不变的）
        std::ofstream cur_ofs_; // 当前文件流（动态变化的）
        size_t cur_size_;       // 当前写入文件的字节数
        size_t max_size_;       // 滚动文件的临界字节数

    public:
        RollBySizeLogSink(const std::string &prefix, size_t max_size = default_max_size)
            : prefix_(prefix), cur_size_(0), max_size_(max_size)
        {
            // 1.目录的创建
            std::string dir = util::File::getDirctory(prefix_);
            if (!util::File::exist(dir))
            {
                if (!util::File::createDirctory(dir))
                {
                    std::cout << "create dirctory fail..." << std::endl;
                    exit(1);
                }
            }
            // 2.创建文件
            createFile();
        }
        ~RollBySizeLogSink()
        {
            if (cur_ofs_.is_open())
                cur_ofs_.close();
        }

        void sink(const char *log, size_t len)
        {
            if (cur_size_ >= max_size_)
            {
                // 滚动到下一个文件
                if (cur_ofs_.is_open())
                {
                    cur_ofs_.close();
                    cur_size_ = 0;
                }
                createFile();
            }
            cur_ofs_.write(log, len);
            cur_size_ += len;
        }

    private:
        void createFile()
        {
            static int count = 0;
            // 1.在prefix的基础上，得到一个完整的文件名
            // filename: prefix + 202311291817550.log
            std::stringstream ss;
            ss << prefix_;
            util::Date d;
            ss << d.year << d.month << d.day << d.hour << d.min << d.sec;
            ss << "-" << count++;
            ss << ".log";
            std::string filename = ss.str();

            // 2.根据文件名打开文件(在得到的目录下创建并打开)
            cur_ofs_.open(filename, std::ofstream::binary | std::ofstream::app);
        }
    };

    class LogSinkFactory
    {
    public:
        template <class LogSinkType, class... Args>
        static LogSink::Ptr create(Args &&...args)
        {
            return std::make_shared<LogSinkType>(std::forward<Args>(args)...);
        }
    };

}; /*namespace ckflogs*/